﻿using System.Net;
using System.Security.Claims;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Primitives;
using Newtonsoft.Json;
using OpenIddict.Abstractions;
using OpenIddict.EntityFrameworkCore.Models;
using OpenIddict.Server.AspNetCore;
using WT.OpenIddict.Application.Contracts.Authorization;
using WT.OpenIddict.Domain.Services.Exceptions;
using WT.OpenIddict.Domain.User;
using static OpenIddict.Abstractions.OpenIddictConstants;

namespace WT.OpenIddict.WebApi.Controllers;

public class AuthorizationController : ControllerBase
{
    private readonly IOpenIddictApplicationManager _applicationManager;

    private readonly IAuthorizationAppService _appService;
    private readonly IOpenIddictAuthorizationManager _authorizationManager;
    private readonly IOpenIddictTokenManager _openIddictTokenManager;
    private readonly IOpenIddictScopeManager _scopeManager;
    private readonly SignInManager<UserDetails> _signInManager;
    private readonly UserManager<UserDetails> _userManager;

    public AuthorizationController(IAuthorizationAppService appService,
        IOpenIddictApplicationManager applicationManager, IOpenIddictAuthorizationManager authorizationManager,
        IOpenIddictScopeManager scopeManager, UserManager<UserDetails> userManager,
        SignInManager<UserDetails> signInManager, IConfiguration configuration,
        IOpenIddictTokenManager openIddictTokenManager)
    {
        _appService = appService;
        _applicationManager = applicationManager;
        _authorizationManager = authorizationManager;
        _scopeManager = scopeManager;
        _userManager = userManager;
        _signInManager = signInManager;
        Configuration = configuration;
        _openIddictTokenManager = openIddictTokenManager;
    }

    public IConfiguration Configuration { get; set; }

    [HttpPost("~/connect/token")]
    [Produces("application/x-www-form-urlencoded")]
    public async Task<IActionResult> Exchange()
    {
        var request = HttpContext.GetOpenIddictServerRequest();
        var principal = new ClaimsPrincipal();

        //客户端凭证
        if (request.IsClientCredentialsGrantType())
        {
            principal = await _appService.GetClientCredentialsIdentityAsync(request.ClientId);
            return SignIn(principal, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);
        }
        //资源所有者

        if (request.IsPasswordGrantType())
        {
            var signInResult =
                await _appService.GetPasswordIdentityAsync(request.GetScopes(), request.Username, request.Password);
            if (!signInResult.CanSignIn)
                throw new ForbiddenException(signInResult.Message, HttpStatusCode.Forbidden.ToString());

            principal = signInResult.Principal;
            await AddCustomClaims(principal);
            principal.SetAudiences(request.ClientId);
            // 存 Cookie 时用同一个 principal
            await HttpContext.SignInAsync(
                "CustomCookie",
                principal,
                new AuthenticationProperties
                {
                    IsPersistent = true,
                    ExpiresUtc = DateTimeOffset.UtcNow.AddDays(7)
                });

            return SignIn(principal, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);
        }

        if (request.IsAuthorizationCodeGrantType() || request.IsDeviceCodeGrantType())
        {
            // 【处理授权码、设备码或刷新令牌请求】
            // 从OpenIddict的认证方案中获取与令牌关联的用户声明（Principal）
            // 这个Principal存储在授权码/设备码/刷新令牌中，用于验证用户身份,只从cookie验证，不给前端颁发refresh_token
            // principal = (await HttpContext.AuthenticateAsync("CustomCookie")).Principal;
            //兼容标准流程，从授权码里面验证结果
            var authenticateResult = await HttpContext.AuthenticateAsync(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); 
            principal = authenticateResult.Principal;
            // 根据Principal获取对应的用户实体（例如：从数据库查询用户）
            // 注意：如果需要在用户密码或角色变更时自动失效令牌，应使用以下方法：
            // var user = await _signInManager.ValidateSecurityStampAsync(principal);
            var user = await _userManager.GetUserAsync(principal);
            if (user is null)
                // 用户不存在或令牌无效（例如：令牌过期/被撤销）
                return Forbid(
                    authenticationSchemes: "CustomCookie",
                    properties: new AuthenticationProperties(new Dictionary<string, string>
                    {
                        [OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.InvalidGrant,
                        [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "令牌无效"
                    }!));

            // 检查用户是否允许登录（例如：用户未激活、被封禁等情况）
            if (!await _signInManager.CanSignInAsync(user))
                return Forbid(
                    authenticationSchemes: "CustomCookie",
                    properties: new AuthenticationProperties(new Dictionary<string, string>
                    {
                        [OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.InvalidGrant,
                        [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "用户不允许登录"
                    }!));

            // 设置每个声明的目标位置（如：access_token/id_token）
            // 通过GetDestinations方法决定声明应出现在哪个令牌中
            principal.SetAudiences(request.ClientId);
            await AddCustomClaims(principal);
            // 触发OpenIddict生成新的访问令牌和身份令牌
            // principal中的声明将根据SetDestinations的设置被包含在对应令牌中

            return SignIn(principal, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);
        }
        //刷新token使用

        if (request.IsRefreshTokenGrantType())
        {
            principal = (await HttpContext.AuthenticateAsync("CustomCookie")).Principal;
            if (string.IsNullOrEmpty(request.RefreshToken))
                return Forbid(
                    authenticationSchemes: "CustomCookie",
                    properties: new AuthenticationProperties(new Dictionary<string, string>
                    {
                        [OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.InvalidGrant,
                        [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "无效的用户凭证"
                    }));
            await AddCustomClaims(principal);
            // 下面的逻辑无需修改，OpenIddict会自动检测refresh_token合法性
            return SignIn(principal, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);
        }
        //微信扫码登陆
        // else if (request.IsWxLoginGrantType())
        // {
        // //取到扫码得到的
        // var code = request.Code;
        // if (code == null)
        // {
        //     throw new UserFriendlyException("code不能为空");
        // }
        // principal = await _appService.LoginByWxAsync(request.GetScopes(),code);
        // return SignIn(principal, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);
        // }

        throw new NotImplementedException("The specified grant is not implemented.");
    }

    private async Task AddCustomClaims(ClaimsPrincipal principal)
    {
        var userIdClaim = principal.FindFirst("sub")?.Value;
        if (string.IsNullOrEmpty(userIdClaim)) throw new ForbiddenException("凭证不存在");
        var userInfo = await _userManager.FindByIdAsync(userIdClaim);
        var roles = await _userManager.GetRolesAsync(userInfo);
        var identity = (ClaimsIdentity)principal.Identity;

        AddClaimWithDestinations(identity, Claims.PhoneNumber, userInfo.PhoneNumber ?? string.Empty, principal);
        AddClaimWithDestinations(identity, Claims.Nickname, userInfo.NickName ?? string.Empty, principal);
        AddClaimWithDestinations(identity, Claims.Username, userInfo.UserName, principal);
        AddClaimWithDestinations(identity, Claims.PhoneNumberVerified, userInfo.PhoneNumberConfirmed.ToString(),
            principal);
        AddClaimWithDestinations(identity, Claims.EmailVerified, userInfo.EmailConfirmed.ToString(), principal);
        // AddClaimWithDestinations(identity, Claims.Picture, tempUrl, principal);
        AddClaimWithDestinations(identity, Claims.Role, JsonConvert.SerializeObject(roles), principal);
        // AddClaimWithDestinations(identity, "permissions", JsonConvert.SerializeObject(permissions), principal);
    }

    private void AddClaimWithDestinations(ClaimsIdentity identity, string type, string value, ClaimsPrincipal principal)
    {
        // 移除已有的同类型 Claim
        var existingClaims = identity.FindAll(type).ToList();
        foreach (var existing in existingClaims) identity.RemoveClaim(existing);

        var claim = new Claim(type, value ?? string.Empty);
        claim.SetDestinations(GetDestinations(claim, principal));
        identity.AddClaim(claim);
    }

    [IgnoreAntiforgeryToken] // 忽略反CSRF令牌验证（授权端点为公开端点）
    [HttpGet("~/connect/authorize")]
    [HttpPost("~/connect/authorize")]
    public async Task<IActionResult> Authorize()
    {
        // 获取OpenID Connect授权请求对象
        var request = HttpContext.GetOpenIddictServerRequest() ??
                      throw new InvalidOperationException("无法获取OpenID Connect请求");

        // 从认证Cookie中获取用户Principal（用户身份信息）
        var result = await HttpContext.AuthenticateAsync("CustomCookie");
        if (result is null || !result.Succeeded || !result.Principal.Identity.IsAuthenticated)
        {
            // 用户未登录的情况处理
            if (request.HasPrompt(Prompts.None))
                // 客户端要求无提示认证但用户未登录，返回登录所需错误
                return Forbid(
                    authenticationSchemes: "CustomCookie",
                    properties: new AuthenticationProperties(new Dictionary<string, string>
                    {
                        [OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.LoginRequired,
                        [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "用户未登录"
                    }!));
            //return Ok(new { success = true, redirectUrl = GenerateFrontendLoginUrl(Request) });
            return Redirect(GenerateFrontendLoginUrl(Request));
        }

        // 处理prompt=login参数：强制重新认证，切换账号使用
        if (request.HasPrompt(Prompts.Login))
        {
            // 移除prompt=login参数避免循环重定向（例如：用户登录后再次跳转到登录页）
            var prompt = string.Join(" ", request.GetPrompts().Remove(Prompts.Login));

            // 构造新的请求参数集合（排除prompt=login参数）
            var parameters = Request.HasFormContentType
                ? Request.Form.Where(p => p.Key != Parameters.Prompt).ToList()
                : Request.Query.Where(p => p.Key != Parameters.Prompt).ToList();
            parameters.Add(new KeyValuePair<string, StringValues>(Parameters.Prompt, prompt));

            //重定向到登录页
            return Redirect(Configuration["VUE:BaseUrl"] + Configuration["VUE:LoginUrl"] +
                            QueryString.Create(parameters));
        }

        // 处理max_age参数：验证会话时效性
        if (request.MaxAge.HasValue &&
            result.Properties?.IssuedUtc.HasValue == true &&
            DateTimeOffset.UtcNow - result.Properties.IssuedUtc.Value >
            TimeSpan.FromSeconds(request.MaxAge.Value))
        {
            // 会话已过期
            if (request.HasPrompt(Prompts.None))
                // 无提示模式返回登录错误
                return Forbid(
                    authenticationSchemes: "CustomCookie",
                    properties: new AuthenticationProperties(new Dictionary<string, string>
                    {
                        [OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.LoginRequired,
                        [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "用户会话已过期"
                    }!));

            // 强制重新认证
            //return Ok(new { success = true, redirectUrl = GenerateFrontendLoginUrl(Request) });
            return Redirect(GenerateFrontendLoginUrl(Request));
        }

        // 获取当前登录用户实体
        var user = await _userManager.GetUserAsync(result.Principal) ??
                   throw new InvalidOperationException("无法获取用户信息");

        // 根据客户端ID获取应用信息（如第三方应用）
        var application = await _applicationManager.FindByClientIdAsync(request.ClientId) ??
                          throw new InvalidOperationException("无法获取客户端应用信息");

        // 查询用户与当前应用的永久授权记录
        var authorizations = await _authorizationManager.FindAsync(
            await _userManager.GetUserIdAsync(user), // 用户唯一标识
            await _applicationManager.GetIdAsync(application), // 应用唯一标识
            Statuses.Valid, // 仅有效状态的授权
            AuthorizationTypes.Permanent, // 持久化授权（非一次性）
            request.GetScopes() // 当前请求的作用域
        ).ToListAsync();

        // 根据应用的同意类型决定授权流程
        switch (await _applicationManager.GetConsentTypeAsync(application))
        {
            // 外部授权类型（如管理员预授权）且无有效授权时返回错误
            case ConsentTypes.External when authorizations.Count == 0:
                return Forbid(
                    authenticationSchemes: "CustomCookie",
                    properties: new AuthenticationProperties(new Dictionary<string, string>
                    {
                        [OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.ConsentRequired,
                        [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] =
                            "用户无权访问该客户端应用"
                    }));

            // 隐式授权或已有有效授权时直接签发令牌
            case ConsentTypes.Implicit:
            case ConsentTypes.External when authorizations.Count != 0:
            case ConsentTypes.Explicit when
                authorizations.Count != 0 &&
                !request.HasPrompt(Prompts.Consent):
                // 创建包含用户声明的Principal对象
                var principal = await _signInManager.CreateUserPrincipalAsync(user);

                // 设置请求的作用域和相关资源
                principal.SetScopes(request.GetScopes());
                principal.SetResources(
                    await _scopeManager.ListResourcesAsync(principal.GetScopes()).ToListAsync());

                // 创建或获取持久化授权记录
                var authorization = authorizations.LastOrDefault()
                                    ?? await _authorizationManager.CreateAsync(
                                        principal,
                                        await _userManager.GetUserIdAsync(user),
                                        await _applicationManager.GetIdAsync(application),
                                        AuthorizationTypes.Permanent,
                                        principal.GetScopes());

                // 将授权ID绑定到Principal
                principal.SetAuthorizationId(
                    await _authorizationManager.GetIdAsync(authorization));

                // 设置声明的令牌目标（如id_token/access_token）
                foreach (var claim in principal.Claims)
                    claim.SetDestinations(GetDestinations(claim, principal));

                return SignIn(principal, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);

            // 需要用户同意但使用prompt=none时返回错误
            case ConsentTypes.Explicit when request.HasPrompt(Prompts.None):
            case ConsentTypes.Systematic when request.HasPrompt(Prompts.None):
                return Forbid(
                    authenticationSchemes: "CustomCookie",
                    properties: new AuthenticationProperties(new Dictionary<string, string>
                    {
                        [OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.ConsentRequired,
                        [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] =
                            "需要用户交互式同意"
                    }));

            // 默认情况应显示同意页面（当前未实现）
            default:
                // TODO: 实现用户同意页面，带参数重定向到前端页面授权同意页面
                // 示例：展示应用名称和请求的作用域，让用户选择是否授权
                var fullUrl = Configuration["VUE:BaseUrl"] + Configuration["VUE:AuthUrl"] +
                              QueryString.Create(
                                  Request.HasFormContentType
                                      ? Request.Form.ToList()
                                      : Request.Query.ToList());
                return Redirect(fullUrl);
            //return Ok(new { success = true, redirectUrl = fullUrl });
        }
    }

    private string GenerateFrontendLoginUrl(HttpRequest request)
    {
        return Configuration["VUE:BaseUrl"] + Configuration["VUE:LoginUrl"] +
               QueryString.Create(
                   request.HasFormContentType
                       ? request.Form.ToList()
                       : request.Query.ToList());
    }

    [Authorize(AuthenticationSchemes = "CustomCookie")] // 明确指定方案// 需要用户已通过身份验证
    //[ValidateAntiForgeryToken] // 防止CSRF攻击（跨站请求伪造）
    //[HttpPost("~/connect/authorize/confirm"), Produces("application/x-www-form-urlencoded")]
    [HttpGet("~/connect/authorize/confirm")]
    [Produces("application/x-www-form-urlencoded")]
    public async Task<IActionResult> AuthorzieAcceptOrDeny([FromQuery] bool accept)
    {
        //拒绝授权则返回403
        if (!accept)
        {
            var fullUrl = Configuration["VUE:BaseUrl"] + Configuration["VUE:Authorize"] +
                          QueryString.Create(
                              Request.HasFormContentType
                                  ? Request.Form.ToList()
                                  : Request.Query.ToList());
            return await LogoutByConcelAuthorize(fullUrl);
        }

        // 获取OpenID Connect授权请求对象
        var request = HttpContext.GetOpenIddictServerRequest() ??
                      throw new InvalidOperationException("无法获取OpenID Connect请求");
        // 获取当前登录用户的实体对象
        var user = await _userManager.GetUserAsync(User) ??
                   throw new InvalidOperationException("无法获取用户信息");

        // 根据客户端ID获取应用信息（如第三方应用）
        var application = await _applicationManager.FindByClientIdAsync(request.ClientId) ??
                          throw new InvalidOperationException("无法获取客户端应用信息");

        // 查询用户与当前应用的永久授权记录（有效且持久化的授权）
        var authorizations = await _authorizationManager.FindAsync(
            await _userManager.GetUserIdAsync(user), // 用户唯一标识
            await _applicationManager.GetIdAsync(application), // 应用唯一标识
            Statuses.Valid, // 仅有效状态的授权
            AuthorizationTypes.Permanent, // 持久化授权（非一次性）
            request.GetScopes() // 当前请求的作用域
        ).ToListAsync();

        // 安全检查：对于外部授权类型，若无有效授权则返回错误
        // 此处重复检查是为了防止恶意用户滥用POST端点绕过授权
        if (authorizations.Count == 0 &&
            await _applicationManager.HasConsentTypeAsync(application, ConsentTypes.External))
            return Forbid(
                authenticationSchemes: "CustomCookie",
                properties: new AuthenticationProperties(new Dictionary<string, string>
                {
                    [OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.ConsentRequired,
                    [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] =
                        "当前用户无权访问该客户端应用"
                }));

        // 创建包含用户声明的Principal对象
        var principal = await _signInManager.CreateUserPrincipalAsync(user);

        // 设置请求的作用域和相关资源
        principal.SetScopes(request.GetScopes()); // 绑定请求的作用域（如profile、email）
        principal.SetResources(
            await _scopeManager.ListResourcesAsync(principal.GetScopes()).ToListAsync()
        ); // 绑定作用域对应资源（如API端点）

        // 创建或获取持久化授权记录（避免用户重复授权）
        var authorization = authorizations.LastOrDefault()
                            ?? await _authorizationManager.CreateAsync(
                                principal,
                                await _userManager.GetUserIdAsync(user),
                                await _applicationManager.GetIdAsync(application),
                                AuthorizationTypes.Permanent,
                                principal.GetScopes()
                            );

        // 将授权ID绑定到Principal（用于后续令牌关联）
        principal.SetAuthorizationId(
            await _authorizationManager.GetIdAsync(authorization)
        );

        // 设置声明的目标位置（id_token或access_token）
        foreach (var claim in principal.Claims) claim.SetDestinations(GetDestinations(claim, principal));
        // 触发OpenIddict生成访问/身份令牌并返回响应
        return SignIn(principal, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);
    }

    [Authorize(AuthenticationSchemes = "CustomCookie")]
    //[ValidateAntiForgeryToken] // 防止CSRF攻击（跨站请求伪造）
    [HttpPost("~/connect/logout")] // 绑定到POST请求的路由路径 /connect/logout
    [HttpGet("~/connect/logout")] // 绑定到POST请求的路由路径 /connect/logout
    public async Task<IActionResult> LogoutPost()
    {
        // 清除本地和外部身份验证的Cookie（例如用户通过Google/Facebook登录后生成的Cookie）
        await _signInManager.SignOutAsync();
        var request = HttpContext.GetOpenIddictServerRequest() ??
                      throw new InvalidOperationException("无法获取OpenID Connect请求");
        // 触发OpenIddict的登出流程，重定向用户到客户端指定的post_logout_redirect_uri
        // 或使用此处设置的默认RedirectUri（"/"）
        var postLogoutRedirectUri = string.Empty;
        if (request.PostLogoutRedirectUri != null)
        {
            postLogoutRedirectUri = request.PostLogoutRedirectUri;
        }
        else
        {
            var application = await _applicationManager.FindByClientIdAsync(request.ClientId);
            var stringPostLogoutRedirectUris =
                (application as OpenIddictEntityFrameworkCoreApplication<Guid>).PostLogoutRedirectUris;
            if (stringPostLogoutRedirectUris != null)
            {
                var postLogoutRedirectUris = JsonConvert.DeserializeObject<string[]>(stringPostLogoutRedirectUris);
                postLogoutRedirectUri = postLogoutRedirectUris.FirstOrDefault();
            }
        }

        return SignOut(
            authenticationSchemes: "CustomCookie", // 指定OpenIddict的认证方案
            properties: new AuthenticationProperties
            {
                RedirectUri = postLogoutRedirectUri // 登出后重定向的默认路径
            });
        //return Ok(new
        //{
        //    success = true,
        //    redirectUrl = postLogoutRedirectUri
        //});
    }


    private static IEnumerable<string> GetDestinations(Claim claim, ClaimsPrincipal principal)
    {
        // 注意：默认情况下，声明不会自动包含在访问令牌和身份令牌中。
        // 要允许 OpenIddict 序列化它们，必须为它们指定目标（Destination），明确声明应包含在访问令牌、身份令牌或两者中。
        //暂时全部放到访问令牌里面
        switch (claim.Type)
        {
            case Claims.Name:
                yield return Destinations.AccessToken;

                if (principal.HasScope(Scopes.Profile)) yield return Destinations.IdentityToken;

                yield break;

            case Claims.Email:
                yield return Destinations.AccessToken;

                if (principal.HasScope(Scopes.Email)) yield return Destinations.IdentityToken;

                yield break;

            case Claims.Role:
                yield return Destinations.AccessToken;

                if (principal.HasScope(Scopes.Roles)) yield return Destinations.IdentityToken;
                yield break;
            // 永远不要将安全印章包含在访问令牌和身份令牌中，因为它是一个敏感值。
            case "AspNet.Identity.SecurityStamp":
                yield break;
            default:
                yield return Destinations.AccessToken;
                yield break;
        }
    }

    private async Task<IActionResult> LogoutByConcelAuthorize(string fullUrl)
    {
        // 清除本地和外部身份验证的Cookie（例如用户通过Google/Facebook登录后生成的Cookie）
        await _signInManager.SignOutAsync();
        var request = HttpContext.GetOpenIddictServerRequest() ??
                      throw new InvalidOperationException("无法获取OpenID Connect请求");
        // 触发OpenIddict的登出流程，重定向用户到客户端指定的post_logout_redirect_uri
        // 或使用此处设置的默认RedirectUri（"/"）
        return SignOut(
            authenticationSchemes: "CustomCookie", // 指定OpenIddict的认证方案
            properties: new AuthenticationProperties
            {
                RedirectUri = fullUrl // 登出后重定向的默认路径
            });
    }

    [Authorize(AuthenticationSchemes = OpenIddictServerAspNetCoreDefaults.AuthenticationScheme)]
    [HttpPost("~/connect/userinfo")] // 绑定到POST请求的路由路径
    [HttpGet("~/connect/userinfo")] // 绑定到POST请求的路由路径
    [Produces("application/json")]
    public IActionResult GetUserInfo()
    {
        var token = HttpContext.Request.Headers.Authorization;
        var userId = User.Claims.FirstOrDefault(it => it.Type == "sub").Value;
        var data = _userManager.FindByIdAsync(userId).Result;
        return Ok(data);
    }

    /// <summary>
    ///     撤销令牌端点,用户主动操作,只撤销这一次的token
    /// </summary>
    /// <param name="request"></param>
    /// <returns></returns>
    [Authorize(AuthenticationSchemes = "CustomCookie")]
    [HttpPost("~/connect/revocat")]
    [Produces("application/x-www-form-urlencoded")]
    public async Task<IActionResult> Revoke()
    {
        var request = HttpContext.GetOpenIddictServerRequest() ??
                      throw new InvalidOperationException("无法获取OpenID Connect请求");
        await _openIddictTokenManager.TryRevokeAsync(request.Token);
        return Ok();
    }
}